package cz.vutbr.fit.dnacompress.encoding;

import java.util.HashMap;
import java.util.Iterator;

class chartableentryDNA {
	public long count;
	public String character;
}

/**
 * Nastavba na Huffman.java
 */
public class HuffmanDNA extends Huffman {

	private HashMap< Integer, String> variantsCountTable = null;
	private int huftree[] = null;
	
	HuffmanDNA( HashMap<Integer, String> variantsCountTable) {
		
		this.variantsCountTable = variantsCountTable;
		this.huftree = new int[ALPHABETSIZE];
	}
	
	// Generate a lookuptable with the frequencies of the char in the alphabet
	public void gencounttable (long[] counttable) 
	{
		int i;
		for (i=0;i<counttable.length;i++) counttable[i]=0;
		
		int count = variantsCountTable.keySet().size();
		for(Iterator<Integer> iter = variantsCountTable.keySet().iterator(); iter.hasNext();) {
			counttable[ makeunsigned( iter.next().byteValue())] = count--;
		}
	}
	
	// Generates huffman tree and then calls buildtranstable().
	int generatecode ()
	{
		int i,index,l;
		long addi;
		int j;
		// tabulka s pocty
		long [] counttable = new long[ALPHABETSIZE];
		// inicializace prekladove tabulky
		for (i=0;i<ALPHABETSIZE;i++) transtable[i] = new String();
		// druha tabulka s pocty vyskytu
		chartableentry[] chartable = new chartableentry[ALPHABETSIZE];
		// pole uzlu = strom ma 2*velikost abecedy
		node[] tree=new node[ALPHABETSIZE*2];
		// z dat je vypocitana tabulka vyskytu
		gencounttable(counttable);
		// z tabulky poctu je vytvorena tabulka vyskytu > 0
		usedchars=0;
		for (i=0;i<ALPHABETSIZE;i++) {
			chartable[i] = new chartableentry();
			if (counttable[i]!=0) {
				chartable[usedchars].count=counttable[i];
				chartable[usedchars].character=(short) i;
				usedchars++;
			}	
		}
		// nejmensi pocet u tabulky vyskytu
		index=getlowest(chartable);
		tree[0]=new node(); // We do not use tree[0] but we must
		tree[0].edge1=0;    // initialize for not getting a
		tree[0].edge2=0;    // Null Pointer exception... :(
		j=1;
		// pro kazdy vyskyt
		while (index!=-1) {
			// novy uzel
			tree[j]=new node();
			tree[j].edge1=chartable[index].character;
			addi=chartable[index].count;
			chartable[index].count=0;
			index=getlowest(chartable);
			if (index!=-1) {
				tree[j].edge2=chartable[index].character;
				chartable[index].count+=addi;
				chartable[index].character=(-1)*j;
			} else {
				tree[j].edge2=(-1)*j;
			}
			index=getlowest(chartable);
			j++;

		}
		buildtranstable(tree,j-2,""); 
		i=0;
		l=1;
		while (l<j-1) {
			this.huftree[i]=tree[l].edge1;
			this.huftree[i+1]=tree[l].edge2;
			i+=2;
			l+=1;
		}
		return(j-2);
	}
	
	// ----------------------------- interface ---------------------------
	// compress
	// compresses datain and gives the result in dataout 
	// and the decoding tree in treeout.
	// Returns the length of the resulting code as integer value.
	public byte[] compress(byte[] datain)
	{	
		// generate tree a and translate table
		generatecode();
		// vytvoreni zakodovaneho retezce
		String s = "";
		for (int i=0;i<datain.length;i++) 
			s+=transtable[makeunsigned(datain[i])];
		// delka dat
		int len=s.length()/8;
		if ((s.length()%8)>0) len++;
		// vytvoreni byte[]
		byte dataout[] = new byte[len];
		// naplneni byte[] z retezce 0 a 1
		String2Bytes( s, dataout);
		
		return dataout;
	}
	
	// uncompresses datain using tree into dataout.
	// dataout will be filled up exactly (-> may not be longer than code expected!)
	public byte[] uncompress(byte[] datain)
	{
		byte dataout[] = new byte[ datain.length * 3];
		int i;
		String s="";
		for (i=0;i<datain.length;i++) s+=dual(makeunsigned(datain[i]));		
		int outcount = uncode(s,dataout);
		
		byte dataout2[] = new byte[outcount - 1];
		System.arraycopy( dataout, 0, dataout2, 0, outcount - 1);
		
		return dataout2;
	}
	
	// ----------------------------- uncompress --------------------------
	// Does what name says...
	int uncode(String s, byte[] dataout)
	{
		int outcount=0;
		int incount=0;
		int treesize=0;
		int treecount=0;
		
		generatecode();
		
		System.out.println( s.length());
		
		while (!((this.huftree[treesize]==0) && (this.huftree[treesize+1]==0))) treesize++;
		treecount=treesize-2;
		while ((outcount<dataout.length) && (incount<s.length())) {
			if (s.charAt(incount)=='0') {
				if (this.huftree[treecount]>=0) {
					dataout[outcount]=(byte) this.huftree[treecount];
					treecount=treesize-2;
					outcount++;
				} else {
					treecount=(Math.abs(this.huftree[treecount]*2)-2);
				} 
			}
			if (s.charAt(incount)=='1') {
				if (this.huftree[treecount+1]>=0) {
					dataout[outcount]=(byte) this.huftree[treecount+1];
					treecount=treesize-2;
					outcount++;
				} else {
					treecount=(Math.abs(this.huftree[treecount+1])*2)-2;
				} 			
			}
			incount++;
		}
		
		return outcount;
	}
}
